/*
 * MiVentanaRobot.cpp
 *
 *  Created on: 26/03/2013
 *      Author: gonzalo
 */

#include "MiVentanaEdificio.h"

#include "glUtils/ProgramBuilder.h"
#include "exceptions/all_exceptions.h"

#include "TextureLoader.h"

#define SEGMENTOS_CILINDRO (10)
#define DEBUG_MI_VENTANA_ROBOT 0
#define CANT_PISOS (5) //Ojo que esto es x 2

void addPoint(vector<float>* vector,float x,float y, float z){
	vector->push_back(x);
	vector->push_back(y);
	vector->push_back(z);
}


vector<float>* createPoints(void){
	//Aqui se crea la superficie que se barre(puntos de control)
	vector<float>* PuntosControl = new vector<float>();

	 int e=1;

	 addPoint(PuntosControl,    0,  0, 0);
	 addPoint(PuntosControl,    0.5*e,  0, 0*e);
	 addPoint(PuntosControl,    1*e, 0, 0);
	 addPoint(PuntosControl,    1*e, 0, 1*e);
	 addPoint(PuntosControl,  0.5*e, 0, 1*e);
	 addPoint(PuntosControl,  0, 0, 1*e);
	 addPoint(PuntosControl,    0, 0, 0);
	 /*addPoint(PuntosControl,    0*e,   2*e, 0);

	 addPoint(PuntosControl,    0*e,   2*e, 0);
	 addPoint(PuntosControl,    0*e,   2*e, 0);
	 addPoint(PuntosControl,   -1*e,   2*e, 0);
	 addPoint(PuntosControl, -0.5*e, 1.5*e, 0);
	 addPoint(PuntosControl, -0.5*e,   1*e, 0);
	 addPoint(PuntosControl,   -1*e,   1*e, 0);
	 addPoint(PuntosControl,   -1*e,   0*e, 0);
	 addPoint(PuntosControl,   -1*e,  -1*e, 0);
	 addPoint(PuntosControl,    0*e,  -1*e, 0);*/

	 return PuntosControl;
}

MiVentanaEdificio::MiVentanaEdificio() {

	this->SetSize(640, 480);
	this->mouse_trap = false;
	this->just_warped = false;

	centerX = glutGet(GLUT_WINDOW_WIDTH) / 2;
	centerY = glutGet(GLUT_WINDOW_HEIGHT) / 2;
	mouse_motion_speed = 0.15;
	camera_motion_speed = 0.5;
	ancho_cubos = 0.1;
	largo_cubos = 1.5;
	altura_camara_2 = 0.8;

	camara_actual = &camara1;

	zoom_camara_4 = 52.0f;

	AmbientalLightIntensity = glm::vec3(0.5f, 0.5f, 0.5f);
	LightPosition = glm::vec4(8.0f, 38.0f, 50.0f, 1.0f);
	//LightPosition = glm::vec4(8.0f, 3.0f, 5.0f, 1.0f);
	LightDirection = glm::vec2(-40.6, 262.0);
	//LightDirection = glm::vec2(-32.6, 192.0);

	znear = 10.0;
	zfar = 500;
}

MiVentanaEdificio::~MiVentanaEdificio() {
	//delete()
}

GLfloat ctrlpoints[4][4][3] = {
		{ { 0.0, 4.5, 0.0 }, { 1.5, 4.5, 0.0 }, { 3.0, 4.5, 0.0 }, { 4.5, 4.5, 0.0 } }, { { 0.0, 3.0, 0.0 }, {
				1.5, 3.0, 3.0 }, { 3.0, 3.0, 3.0 }, { 4.5, 3.0, 0.0 } }, { { 0.0, 1.5, 0.0 },
				{ 1.5, 1.5, 3.0 }, { 3.0, 1.5, 3.0 }, { 4.5, 1.5, 0.0 } }, { { 0.0, 0.0, 0.0 }, { 1.5, 0.0,
				0.0 }, { 3.0, 0.0, 0.0 }, { 4.5, 0.0, 0.0 } } };

float puntosSupRev[18] = { 0.0, 3.0, 0, 1.0, 2.5, 0.0, 1.5, 2.0, 0.0, 2.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0,
		-2.0, 0.0 };

void MiVentanaEdificio::OnResize(int w, int h) {
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	//this->width = w;
	//this->height = h;

	centerX = glutGet(GLUT_WINDOW_WIDTH) / 2;
	centerY = glutGet(GLUT_WINDOW_HEIGHT) / 2;
}

void MiVentanaEdificio::key_handle_move_camera(int nKey, char cAscii) {

	switch (cAscii) {
	case 'w':
	case 'W':
		if (camara_actual == &camara1) {
			//camara_actual->translate_forward(camera_motion_speed);
			camara_actual->moveCameraUp(0.0);
		} else if (camara_actual == &camara2) {
			camara_actual->moveCamera(0.0);

			//camara_actual->translate_forward_z_constant(camera_motion_speed);
			//camara_actual->set_z_position(altura_camara_2);
		}
		break;
	case 's':
	case 'S':
		if (camara_actual == &camara1) {
			//camara_actual->translate_back(camera_motion_speed);
			camara_actual->moveCameraUp(180.0);
		} else if (camara_actual == &camara2) {
			//camara_actual->translate_back_z_constant(camera_motion_speed);
			camara_actual->moveCamera(180.0);
		}
		break;
	case 'a':
	case 'A':
		if (camara_actual == &camara1) {
			//camara_actual->translate_left(camera_motion_speed);
			camara_actual->moveCamera(90.0);
		} else if (camara_actual == &camara2) {
			//camara_actual->translate_left(camera_motion_speed);
			camara_actual->moveCamera(90.0);
		}
		break;
	case 'd':
	case 'D':
		if (camara_actual == &camara1) {
			//camara_actual->translate_right(camera_motion_speed);
			camara_actual->moveCamera(270.0);
		} else if (camara_actual == &camara2) {
			//camara_actual->translate_right(camera_motion_speed);
			camara_actual->moveCamera(270.0);
		}
		break;
	case '1':
		camara_actual = &camara1;
		break;
	case '2':
		camara_actual = &camara2;
		camara_actual->set_position(
				glm::vec3(camara_actual->getLocation().x, camara_actual->getLocation().y, altura_camara_2));
		break;
	case '3':
		camara_actual = &camara3;
		break;
	case '4':
		camara_actual = &camara4;
		break;
	case '0':
		camara1.set_position(glm::vec3(LightPosition));
		camara1.lookAt(LightDirection.x, LightDirection.y);
		break;
	default:
		key_handle_traslate_object(nKey, cAscii);
		break;
	}

}

void MiVentanaEdificio::key_handle_traslate_object(int nKey, char cAscii) {
	switch (cAscii) {
	case 'i':
	case 'I':
		//camara_actual->translate_forward(camera_motion_speed);
		break;
	case 'k':
	case 'K':
		//camara_actual->translate_back(camera_motion_speed);
		break;
	case 'l':
	case 'L':
		//camara_actual->translate_left(camera_motion_speed);
		break;
	case 'j':
	case 'J':
		//camara_actual->translate_right(camera_motion_speed);
		break;
	}
}

void MiVentanaEdificio::key_handle_arrows(int nKey, char cAscii) {

	glm::mat4 model_matrix = glm::mat4(1.0f);
	switch (nKey) {
	case 0x65:
		//LightPosition.x += 0.1;
		//znear += 1.0;
		break;
	case 0x67:
		//LightPosition.x -= 0.1;
		//znear -= 1.0;
		break;
	case 0x64:
		torre->girar_grua(-1.0);
		break;
	case 0x66:
		torre->girar_grua(1.0);
		break;
	}
	std::cout << "Znear " << znear << " Zfar " << zfar << std::endl;
}

void MiVentanaEdificio::OnKeyDown(int nKey, char cAscii) {
	if (nKey == 0) {
		switch (cAscii) {
		case 27:
			if (this->mouse_trap) {
				this->mouse_trap = false;
				glutSetCursor(GLUT_CURSOR_INHERIT);
			} else {
				this->Close();
			}
			break;
		default:
			key_handle_move_camera(nKey, cAscii);
			break;
		}
	} else {
		key_handle_arrows(nKey, cAscii);
	}
	this->Repaint();
}

void MiVentanaEdificio::OnKeyUp(int nKey, char cAscii) {
	/*if (cAscii == 'f') {
	 SetFullscreen(true);
	 } else if (cAscii == 'w') {
	 SetFullscreen(false);
	 }*/
}

void MiVentanaEdificio::init_cameras() {
	// Establezco las posiciones iniciales de las 2 camaras
	camara1.set_position(glm::vec3(-6.0, 6.0, altura_camara_2));
	camara2.set_position(glm::vec3(-6.0, 5.0, altura_camara_2));
	camara4.set_position(glm::vec3(14.0, 0.0, 38.0));
	camara4.lookAt(0.0, 0.0);

	camara1.perspective_projection(50, 640.0 / 480.0, 0.1, 10000);
	camara2.perspective_projection(50, 640.0 / 480.0, 0.1, 10000);
	camara3.perspective_projection(50, 640.0 / 480.0, 0.1, 10000);
	camara4.perspective_projection(50, 640.0 / 480.0, 0.1, 10000);

	//camara_actual = &camara2;
	camara_actual = &camara1;
}

void MiVentanaEdificio::init_skybox() {
	/*std::string filenames[6] = { std::string("skybox/1front.jpg"), std::string("skybox/1back.jpg"),
	 std::string("skybox/1left.jpg"), std::string("skybox/1right.jpg"), std::string("skybox/1top.jpg"),
	 std::string("skybox/1bottom.jpg") };*/
	std::string filenames[6] = { std::string("skybox/2front.jpg"), std::string("skybox/2back.jpg"),
			std::string("skybox/2left.jpg"), std::string("skybox/2right.jpg"), std::string("skybox/1top.jpg"),
			std::string("skybox/2bottom.jpg") };

	cubemap_texture = TextureLoader::load_cube_map_texture(filenames);

	skyboxCube = new Cube(skyboxShader->get_program());
	skyboxCube->set_type(GL_QUADS);
}

void MiVentanaEdificio::init_shaders() {
	skyboxShader = new Shader("shaders/skyboxShader.vert", "shaders/skyboxShader.frag");
	basicShader = new Shader("shaders/shader.vert", "shaders/shader.frag");
	lightingShaderDefor = new Shader("shaders/lightingShaderDeform.vert", "shaders/lightingShader.frag");
	lightingShader = new Shader("shaders/lightingShader.vert", "shaders/lightingShader.frag");
	reflectionShader = new Shader("shaders/reflection.vert", "shaders/reflection.frag");
	refractionShader = new Shader("shaders/refraction.vert", "shaders/refraction.frag");
	shadowShader = new Shader("shaders/shadowShader.vert", "shaders/shadowShader.frag");
	simpleShader = new Shader("shaders/simpleShader.vert", "shaders/simpleShader.frag");
}

void MiVentanaEdificio::init_textures() {
	textura_piso = TextureLoader::load_texture("piso.jpg");
	textura_columnas = TextureLoader::load_texture("columna.jpg");
	textura_escalera = TextureLoader::load_texture("escalera.jpg");
	textura_ladrillo = TextureLoader::load_texture("brick.jpg");
	textura_pasto = TextureLoader::load_mip_map_texture("grass.jpg");
	textura_viga = TextureLoader::load_texture("viga.jpg");
	textura_piedra = TextureLoader::load_texture("stone.jpg");
	textura_arena = TextureLoader::load_texture("sand.jpg");
	textura_tronco = TextureLoader::load_texture("tronco.jpg");
	textura_metal = TextureLoader::load_texture("steel2.jpg");
}

void MiVentanaEdificio::generate_shadow_buffer() {
	shadowMapWidth = 12 * 640;
	shadowMapHeight = 12 * 480;

	GLfloat border[] = { 1.0f, 0.0f, 0.0f, 0.0f };

	//The shadow maptexture
	glGenTextures(1, &shadowMapTexture);
	glBindTexture(GL_TEXTURE_2D, shadowMapTexture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, shadowMapWidth, shadowMapHeight, 0,
			GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);

	//Assign the shadow map to texture channel 0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, shadowMapTexture);

	//Create and set up the FBO
	glGenFramebuffers(1, &shadowFBO);
	glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMapTexture, 0);

	// Set the target for the fragment shader outputs
	GLenum drawBuffers[] = { GL_NONE };
	glDrawBuffers(1, drawBuffers);

	// Unbind the framebuffer, and revert to default framebuffer
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void MiVentanaEdificio::OnInit() {

	init_cameras();

	glClearColor(0.3f, 0.3f, 0.4f, 0.0f);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);

	// Creo los programas a partir de los Shaders

	init_shaders();

	basicShader->use_shader();

	init_skybox();

	init_textures();

	lightingShader->use_shader();

	torre = new Torre(lightingShader->get_program(), largo_cubos, ancho_cubos);
	torre->traslate(glm::vec3(5.0f, 10.0f, 0.0f));
	torre->scale(glm::vec3(1.0f, 1.0f, 1.5f));
	torre->set_color(glm::vec3(0.8, 0.8, 0.2));

	edificio = new Edificio(lightingShader->get_program(), CANT_PISOS);
	edificio->set_textures(textura_piso, textura_escalera, textura_columnas);

	// Para que se repita la textura mas alla del 1.0
	glBindTexture(GL_TEXTURE_2D, textura_ladrillo);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glBindTexture(GL_TEXTURE_2D, 0);

	pared_exterior = new Rectangle2D(lightingShader->get_program());
	pared_exterior->traslate(glm::vec3(6.5f, 2.9f, 3.0f * CANT_PISOS));
	pared_exterior->scale(glm::vec3(4.0f, 1.0f, CANT_PISOS * 6.0f));
	pared_exterior->rotate(90, glm::vec3(1, 0, 0));

	std::vector<DatosVertice> texturas;
	DatosVertice datos;
	datos.texture = glm::vec2(0.0, 0.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(0.0, 10.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(1.0, 10.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(1.0, 0.0);
	texturas.push_back(datos);
	pared_exterior->change_texture_data(texturas);
	pared_exterior->set_texture_id(textura_ladrillo);

	//suelo = new Floor(lightingShader->get_program());
	//suelo->set_texture_id(textura_piso);

	reflectionShader->use_shader();
	vidrio_exterior = new Rectangle2D(reflectionShader->get_program());
	vidrio_exterior->traslate(glm::vec3(-1.0f, 2.9f, 3.0f * CANT_PISOS));
	vidrio_exterior->scale(glm::vec3(-10.0f, 1.0f, CANT_PISOS * 6.0f));
	vidrio_exterior->rotate(180, glm::vec3(0, 0, 1));
	vidrio_exterior->rotate(90, glm::vec3(1, 0, 0));

	refractionShader->use_shader();
	vidrio_interior = new Rectangle2D(refractionShader->get_program());
	vidrio_interior->traslate(glm::vec3(-1.0f, 2.9f, 3.0f * CANT_PISOS));
	vidrio_interior->scale(glm::vec3(10.0f, 1.0f, CANT_PISOS * 6.0f));
	vidrio_interior->rotate(180, glm::vec3(0, 0, 1));
	vidrio_interior->rotate(90, glm::vec3(1, 0, 0));

	// Creo las figuras normales

	piso_mundo = new Rectangle2D(lightingShader->get_program());
	texturas.clear();

	datos.texture = glm::vec2(100.0, 100.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(-100.0, 100.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(-100.0, -100.0);
	texturas.push_back(datos);
	datos.texture = glm::vec2(100.0, -100.0);
	texturas.push_back(datos);
	piso_mundo->change_texture_data(texturas);
	piso_mundo->set_texture_id(textura_pasto);
	piso_mundo->scale(glm::vec3(2000, 2000.0, 1.0));

	vigas = new Vigas(lightingShader->get_program());
	vigas->set_texture_id(textura_viga);

	//Las dos superficies tiene la misma forma, aunque tiene distintas escalas.
	piedras = new BezierCubSurf(lightingShader->get_program(), ctrlpoints);
	piedras->traslate(glm::vec3(10.0f, 5.0f, 0.0f));
	piedras->scale(glm::vec3(2.0f, 2.0f, 4.0f));
	piedras->set_texture_id(textura_piedra);

	arena = new BezierCubSurf(lightingShader->get_program(), ctrlpoints);
	arena->traslate(glm::vec3(14.0f, 6.0f, 0.0f));
	arena->scale(glm::vec3(2.0f, 2.0f, 3.5f));
	arena->set_texture_id(textura_arena);

	//Cuatro arboles que pueden controlarse independientemente, se puede cambiar a un conjunto.
	arbol1 = new Arbol(lightingShaderDefor->get_program());
	arbol1->traslate(glm::vec3(-15.0f, -15.0f, 2.0f));
	arbol1->set_texture_down(textura_tronco);
	arbol1->set_texture_up(textura_pasto);
	arbol2 = new Arbol(lightingShaderDefor->get_program());
	arbol2->set_texture_down(textura_tronco);
	arbol2->set_texture_up(textura_pasto);
	arbol2->scale(glm::vec3(1.5f, 1.2f, 1.4f));
	arbol2->traslate(glm::vec3(-10.0f, -5.0f, 2.0f));

	vector<float>* formaTubo = createPoints();
	vector<float>* camino1 = new vector<float>();
		addPoint(camino1, 3.0,1.0,3.0);
		addPoint(camino1, 3.0,3.0,3.0);
		addPoint(camino1, 3.0,5.0,3.0);
		addPoint(camino1, 3.0,6.0,3.0);
		addPoint(camino1, 4.0,8.0,3.0);
		addPoint(camino1, 5.0,8.0,3.0);
		addPoint(camino1, 6.0,6.0,3.0);
		addPoint(camino1, 6.0,5.0,3.0);
		addPoint(camino1, 6.0,3.0,3.0);
		addPoint(camino1, 6.0,2.0,3.0);
		addPoint(camino1, 8.0,1.0,3.0);
		addPoint(camino1, 10.0,3.0,3.0);
		addPoint(camino1, 10.0,10.0,3.0);
		//addPoint(camino1, 8.0,1.0,3.0);
		//addPoint(camino1, 8.0,1.0,3.0);


	tuberia = new SweepSurf(lightingShader->get_program(), formaTubo, camino1);
	tuberia->set_texture_id(textura_metal);
	tuberia->traslate(glm::vec3(-3.2,1.5,26.3));
	tuberia->scale(glm::vec3(0.6,0.7,0.8));
	tuberia->scale(glm::vec3(1.0,-1.0,1.0));
	delete(formaTubo);
	delete(camino1);


	/*arbol3 = new Arbol(lightingShader->get_program());
	 arbol3 ->set_texture_down(load_texture("tronco.jpg"));
	 arbol3 ->set_texture_up(grass_texture);
	 arbol3 -> traslate(glm::vec3(11.0f,-13.0f,0.0f));
	 arbol4 = new Arbol(lightingShader->get_program());
	 arbol4 ->set_texture_down(load_texture("tronco.jpg"));
	 arbol4 ->set_texture_up(grass_texture);
	 arbol4 -> traslate(glm::vec3(17.0f,16.0f,0.0f));*/

	generate_shadow_buffer();

	// TEST CUBOS
	cantTestCubes = 4;
	testCubes = new Cube *[4];
	for (uint i = 0; i < cantTestCubes; i++) {
		testCubes[i] = new Cube(lightingShader->get_program());
	}

	testCubes[0]->traslate(glm::vec3(0.0, 3.0, 0.5));
	testCubes[0]->rotate(90, glm::vec3(0.0, 0.0, 1.0));
	testCubes[1]->traslate(glm::vec3(0.0, 3.0, 2.0));
	testCubes[1]->rotate(90, glm::vec3(0.0, 0.0, 1.0));
	testCubes[2]->traslate(glm::vec3(1.5, 3.0, 0.5));
	testCubes[2]->rotate(90, glm::vec3(0.0, 0.0, 1.0));
	testCubes[3]->traslate(glm::vec3(1.5, 3.0, 2.0));
	testCubes[3]->rotate(90, glm::vec3(0.0, 0.0, 1.0));

	testCilinder = new Cilinder(lightingShader->get_program(), 20);
	testCilinder->traslate(glm::vec3(0.0, 0.0, 2.0));

	lightCube = new Cube(lightingShader->get_program());
	lightCube->scale(glm::vec3(0.1, 0.1, 0.1));

	testRectangle = new Rectangle2D(lightingShader->get_program());

	testCubes[0]->set_texture_id(textura_ladrillo);
	testCubes[1]->set_texture_id(textura_ladrillo);
	testCubes[2]->set_texture_id(textura_ladrillo);
	//testCubes[3]->set_texture_id(brick_texture);
	testCubes[3]->set_color(glm::vec3(1.0, 0.0, 0.0));
	testCilinder->set_texture_id(textura_ladrillo);

	glm::mat4 model_matrix = glm::mat4(1.0f);
	glm::vec3 posicion = torre->get_camera_position(model_matrix);
	camara3.set_position(posicion);
	camara3.lookAt(0.0, 0.0);

}

void MiVentanaEdificio::calculate_normal_matrix() {
	// Invalid    NormalMatrix = glm::vec3(ModelMatrix);
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			NormalMatrix[i][j] = ModelMatrix[i][j];

	// Calculo la inversa transpuesta
	NormalMatrix = glm::inverse(NormalMatrix);
	NormalMatrix = glm::transpose(NormalMatrix);

}

void MiVentanaEdificio::bind_camera_projection(unsigned int shader_id) {
	//glm::vec3 projPos = cam.getLocation();
	//glm::vec3 projAt = cam.get;
	//glm::vec3 projUp = glm::vec3(0.0f, 1.0f, 0.0f);
	glm::mat4 projView = LightCamera.getViewMatrix();
	glm::mat4 projProj = LightCamera.getProjectionMatrix();
	glm::mat4 identity = glm::mat4(1.0f);
	glm::mat4 projScaleTrans = glm::translate(identity, glm::vec3(0.5f))
			* glm::scale(identity, glm::vec3(0.5f));
	glm::mat4 m = projScaleTrans * projProj * projView;

	// Set the uniform variable
	GLint loc = glGetUniformLocation(shader_id, "ProjectorMatrix");
	if (loc >= 0) {
		glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]);
	}

}

void MiVentanaEdificio::bind_matrixes(GLuint shader_id) {
	GLuint location;
	// Dependen de la camara
	camara_actual->bind_projection_matrix(shader_id, "ProjectionMatrix");
	camara_actual->bind_view_matrix(shader_id, "ViewMatrix");
	camara_actual->bind_camera_position(shader_id, "cameraPosition");

	//camara_actual->.bind_view_projection_matrix(shader_id, "ViewProjectionMatrix");

	location = glGetUniformLocation(shader_id, "LightPosition");
	if (location >= 0) {
		glUniform3fv(location, 1, &LightPosition[0]);
	}

	location = glGetUniformLocation(shader_id, "AmbientalLightIntensity");
	if (location >= 0) {
		glUniform3fv(location, 1, &AmbientalLightIntensity[0]);
	}

	// Para cada objeto hay que calcular esto

	location = glGetUniformLocation(shader_id, "ModelMatrix");
	if (location >= 0) {
		glUniformMatrix4fv(location, 1, GL_FALSE, &ModelMatrix[0][0]);
	}

	ModelViewMatrix = camara_actual->getViewMatrix() * ModelMatrix;
	location = glGetUniformLocation(shader_id, "ModelViewMatrix");
	if (location >= 0) {
		glUniformMatrix4fv(location, 1, GL_FALSE, &ModelViewMatrix[0][0]);
	}

	ModelViewProjectionMatrix = camara_actual->getViewProjectionMatrix() * ModelMatrix;
	location = glGetUniformLocation(shader_id, "ModelViewProjectionMatrix");
	if (location >= 0) {
		glUniformMatrix4fv(location, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
	}

	calculate_normal_matrix();

	// Bind Normal Matrix
	GLuint location_normal_matrix = glGetUniformLocation(shader_id, "NormalMatrix");
	if (location_normal_matrix >= 0) {
		glUniformMatrix3fv(location_normal_matrix, 1, GL_FALSE, &NormalMatrix[0][0]);
	}
}

void MiVentanaEdificio::draw_skybox() {
	GLuint location;
	GLfloat escala = 10000.0;

	glDisable(GL_CULL_FACE);

	ModelMatrix = glm::mat4(1.0);
	ModelMatrix = glm::scale(ModelMatrix, glm::vec3(escala, escala, escala));
	ModelViewProjectionMatrix = glm::mat4(1.0);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);

	location = glGetUniformLocation(skyboxShader->get_program(), "cubeMap");
	if (location >= 0) {
		glUniform1i(location, 0);
	}

	skyboxShader->use_shader();
	bind_matrixes(skyboxShader->get_program());

	skyboxCube->draw(skyboxShader->get_program(), ModelMatrix);
}

void MiVentanaEdificio::draw_scene(Shader * shader) {

	shader->use_shader();
	GLuint program = shader->get_program();

	bind_matrixes(program);
	ModelMatrix = glm::mat4(1.0f);

	torre->draw(program, ModelMatrix);

	piso_mundo->draw(program, ModelMatrix);

	pared_exterior->draw(program, ModelMatrix);

	edificio->draw(program, ModelMatrix);
	vigas->draw(program, ModelMatrix);
	arena->draw(program, ModelMatrix);
	piedras->draw(program, ModelMatrix);
	arbol1->draw(program, ModelMatrix);
	arbol2->draw(program, ModelMatrix);
	/*arbol3->draw(ModelMatrix);
	 arbol4->draw(ModelMatrix);*/
	tuberia->draw(program, ModelMatrix);

	//suelo->draw(ModelMatrix);
	// TEST
	//for (uint i = 0; i < cantTestCubes; i++) {
	// testCubes[i]->draw(ModelMatrix);
	//}
	//testCilinder->draw(ModelMatrix);
	glm::mat4 matrix = glm::mat4(1.0f);
	lightCube->set_model_matrix(matrix);
	lightCube->traslate(glm::vec3(LightPosition));
	lightCube->scale(glm::vec3(0.1, 0.1, 0.1));
	lightCube->draw(program, ModelMatrix);
}

void MiVentanaEdificio::draw_test_cubes(Shader * shader) {

	shader->use_shader();
	GLuint program = shader->get_program();

	bind_matrixes(program);

	for (uint i = 0; i < cantTestCubes; i++) {
		testCubes[i]->draw(shader->get_program(), ModelMatrix);
	}
	testCilinder->draw(shader->get_program(), ModelMatrix);
	glm::mat4 matrix = glm::mat4(1.0f);
	lightCube->set_model_matrix(matrix);
	lightCube->traslate(glm::vec3(LightPosition));
	lightCube->scale(glm::vec3(0.1, 0.1, 0.1));
	lightCube->draw(shader->get_program(), ModelMatrix);

	piso_mundo->draw(shader->get_program(), ModelMatrix);
}

void MiVentanaEdificio::draw_shadow_map() {
	Camera * camara_anterior;
	GLint location;
	//glEnable(GL_CULL_FACE);
	//glCullFace(GL_BACK);

	ModelMatrix = glm::mat4(1.0f);

	// Bind to texture's FBO
	glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glViewport(0, 0, shadowMapWidth, shadowMapHeight);

	glClear(GL_DEPTH_BUFFER_BIT);

	LightCamera.set_position(glm::vec3(LightPosition));
	LightCamera.lookAt(LightDirection.x, LightDirection.y);
	LightCamera.perspective_projection(80, shadowMapWidth / shadowMapHeight, znear, zfar);

	camara_anterior = camara_actual;
	camara_actual = &LightCamera;

	draw_scene(simpleShader);
	//draw_test_cubes(simpleShader);

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	camara_actual = camara_anterior;
}

void MiVentanaEdificio::draw_mirror() {
	reflectionShader->use_shader();
	bind_matrixes(reflectionShader->get_program());

	glEnable(GL_CULL_FACE);
	glCullFace(GL_FRONT);
	vidrio_exterior->draw(reflectionShader->get_program(), ModelMatrix);

	refractionShader->use_shader();
	bind_matrixes(refractionShader->get_program());

	//glDisable(GL_DEPTH_TEST);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_FRONT);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	vidrio_interior->draw(reflectionShader->get_program(), ModelMatrix);

	glEnable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
}

void MiVentanaEdificio::OnRender() {
	GLint location;

	 // Limpio los buffers de color y depth
	glClear(GL_DEPTH_BUFFER_BIT);

	draw_shadow_map();

	glViewport(0, 0, this->width, this->height); // Viewport for the texture

	glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT);

	draw_skybox();

	shadowShader->use_shader();

	bind_camera_projection(shadowShader->get_program());

	//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, shadowMapTexture);

	location = glGetUniformLocation(shadowShader->get_program(), "ShadowMap"); // Sampler
	if (location >= 0) {
		glUniform1i(location, 1);
	}

	glDisable(GL_CULL_FACE);
	//draw_scene(lightingShader);

	draw_scene(shadowShader);
	//draw_test_cubes(shadowShader);

	draw_mirror();

	// Dibujo el piso del mundo
	//glm::mat4 m = glm::mat4(1.0f);
	//piso_mundo->draw(m);
	// dibujo la torre
	// dibujo el edificio
	//edificio->draw(m);
	//vidrio_interior->draw(model_matrix);
	//vidrio_exterior->draw(model_matrix);
	//pared_exterior->draw(model_matrix);
	//vigas->draw(model_matrix);
	//arbol1->draw(model_matrix);
	//arbol2->draw(model_matrix);
	//arbol3->draw(model_matrix);
	//arbol4->draw(model_matrix);
	//piedras->draw(model_matrix);
	//arena->draw(model_matrix);
	glutSwapBuffers();
}

void MiVentanaEdificio::OnMouseDown(int button, int x, int y) {
	if (button == 0) {
		this->mouse_trap = true;
		glutSetCursor(GLUT_CURSOR_NONE);
	}
}

void MiVentanaEdificio::OnMouseUp(int button, int x, int y) {

}

void MiVentanaEdificio::OnMouseMove(int x, int y) {

	int dx, dy;
	GLfloat deltax = 0, deltay = 0;

	if (this->just_warped) {
		this->just_warped = false;
		return;
	}

	if (this->mouse_trap) {
		dx = x - centerX;
		dy = y - centerY;

		if (dx) {
			deltax = -dx * mouse_motion_speed;
		}
		if (dy) {
			deltay = -dy * mouse_motion_speed;
		}
		/*if (camara_actual != &camara4) {
		 camara_actual->rotateCamera(deltay,deltax);
		 //camara_actual->rotate_direction(deltax);
		 //camara_actual->rotate_pitch(deltay);
		 } else {
		 camara_actual->rotate_on_center(deltax, deltay);
		 }*/
		camara_actual->rotateCamera(deltay, deltax);

		if (x != centerX || y != centerY) {
			this->just_warped = true;
			glutWarpPointer(centerX, centerY);
		}
	}
	this->Repaint();
}

void MiVentanaEdificio::OnLeftMouseDrag(int x, int y) {

}

void MiVentanaEdificio::OnMouseWheel(int nWheelNumber, int nDirection, int x, int y) {

}

void MiVentanaEdificio::OnIdle(void) {
	if (this->_bInit) {
		this->Repaint();
	}
}

